///////////////////////////////////////////////////////////////////////////////////
//  VARIABLES
///////////////////////////////////////////////////////////////////////////////////
int[][] selectedGenreInfo;
//int graphCornerX = 0;
//int graphCornerY = 140;
//int graphWidth = 950;
//int graphHeight = 580;
int yLableOffsetx = 35;
int selectedMonth = 0;
int selectedSeason = 0;
float theAverage = 0.0f;
int[] rangeYears = { 1920, 2009};
String[] months = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", "rohan"};
String[] genres = { "Action", "Adventure", "Animation", "Comedy", "Crime", "Documentary", "Drama", "Fantasy", "Family", "Film-Noir", "Horror", "Musical", "Mystery", "Romance", "Sci-Fi", "Thriller", "War", "Western"};
int[] seasons = { 2, 5, 8, 11};

///////////////////////////////////////////////////////////////////////////////////
//
//  DRAW FUNCTIONS
//
///////////////////////////////////////////////////////////////////////////////////
void drawFullRangeAll()
{
  int totalLastYear = 0;
  float x1 = 0;
  float x2 = 0;
  float y1 = 0;
  float y2 = 0;
  for( int j = 0; j < yearTotals[rangeYears[0] - 1920].length; j++)
  {
    totalLastYear += yearTotals[rangeYears[0] - 1920][j];
  }
  beginShape(LINES);
  for( int i = 1 ; i < rangeYears[1] - rangeYears[0] + 1; i++)
  {
    int totalThatYear = 0;
    for( int j = 0; j < yearTotals[(rangeYears[0] - 1920) + i].length; j++)
    {
      totalThatYear += yearTotals[(rangeYears[0] - 1920) + i][j];
    }
    
    x1 = map( (rangeYears[0] + i - 1), rangeYears[0], rangeYears[1], mapGraphCornerX + yLableOffsetx, mapWidth + mapGraphCornerX );
    y1 = map( totalLastYear, 0, mostInOneYear, mapHeight + mapGraphCornerY, mapGraphCornerY );
    vertex( x1, y1);
    
    x2 = map( (rangeYears[0] + i), rangeYears[0], rangeYears[1], mapGraphCornerX + yLableOffsetx, mapWidth + mapGraphCornerX );
    y2 = map( totalThatYear, 0, mostInOneYear, mapHeight + mapGraphCornerY, mapGraphCornerY );
    vertex( x2, y2);
    totalLastYear = totalThatYear;
  }
  endShape(LINES);
  int yOffset = 0;
  if( rangeYears[1] > 1999)
  {
    textAlign( RIGHT, TOP);
  }
  else if( rangeYears[1] > 1995)
  {
    textAlign( RIGHT, BOTTOM);
  }
  else
  {
    yOffset = -10;
    textAlign( RIGHT, BOTTOM);
  }
  fill(0);
  text("Total for All Genres", x2 - 5, y2 + yOffset);
}

void drawFullRange(String genre)
{
  int[] genreTotals = findData( genre);

  beginShape();
  for( int i = 0; i < genreTotals.length-1 ; i++)
  {
    float x = map( (rangeYears[0] + i), rangeYears[0], rangeYears[1]-1, mapGraphCornerX + yLableOffsetx, mapWidth + mapGraphCornerX);
    float y = map( genreTotals[i], 0, mostInOneYear, mapHeight + mapGraphCornerY, mapGraphCornerY );
    curveVertex( x, y);
    if( (i == 0) || ( i == genreTotals.length - 2))
    {
      curveVertex( x, y);
    }
  }
  endShape();
}

void drawFullRange(String genre1, String genre2)
{
  int[] genreTotals = totalsPerYear( genre1, genre2);

  beginShape();
  for( int i = 0; i < genreTotals.length-1; i++)
  {
    float x = map( (rangeYears[0] + i), rangeYears[0], rangeYears[1]-1, mapGraphCornerX + yLableOffsetx, mapWidth + mapGraphCornerX );
    float y = map( genreTotals[i], 0, mostInOneYear, mapHeight + mapGraphCornerY, mapGraphCornerY );
    curveVertex( x, y);
    if( (i == 0) || ( i == genreTotals.length - 2))
    {
      curveVertex( x, y);
    }
  }
  endShape();
}

///////////////////////////////////////////////////////////////////////////////////////////
//
//  This is where the stack plot is drawn
//
///////////////////////////////////////////////////////////////////////////////////////////
void drawStackPlot( int[] selections)
{
  int yAxisAdjust = 0;
  
  
  if( showTotal)
  {
    strokeWeight(2);
    stroke(0);
    drawFullRangeAll();
  }
  if( !(selections.length >= 1) )
  {
    return;
  }
  //calculate the vertices for the stackplot
  int[][] vertices = new int[selections.length][];
  vertices[0] = subset( selectedGenreInfo[selections[selections.length - 1]], (rangeYears[0] - 1920), (rangeYears[1] - rangeYears[0] + 1) );
  int selectedOffset = rangeYears[0] - 1920;
  for( int i = 1; i < selections.length; i++)
  {
    vertices[i] = new int[rangeYears[1] - rangeYears[0] + 1];
    for( int j = 0; j < vertices[i].length; j++)
    {
      vertices[i][j] = vertices[i-1][j] + selectedGenreInfo[selections[selections.length - 1 - i]][j + selectedOffset];
    }
  }
  
  int theLargestValue = findMax(vertices[vertices.length - 1]);
  if( showTotal && (theLargestValue < mostInOneYear) )
  {
    theLargestValue = mostInOneYear;
  }
  yAxisAdjust = findAdjustment( theLargestValue );
  theLargestValue += yAxisAdjust;
  theLargestValue -= theLargestValue % yAxisAdjust;
  drawLabels( theLargestValue);
  if(showAverage)
  {
    strokeWeight(2);
    stroke(0);
    drawAverage(theLargestValue);
  }
  //draw the found vertices
  strokeWeight(1);
  //draw each region on the next line
  for( int k = 0; k < vertices.length; k++)
  {
//    color genreColor = color(lerp( 0, 255, ( k/ float(vertices.length) ) ), 200, 110);
    
    //draw the next line
    stroke( genreColors[selections[selections.length - 1 - k]] );
    int mUpperBound;
    beginShape(LINES);
    if( vertices[k].length == 1)
    {
      mUpperBound = 1;
    }
    else
    {
      mUpperBound = vertices[k].length-1;
    }
    for( int m = 0; m < mUpperBound; m++)
    {
      if( mUpperBound == 1)
      {
        float x1 = mapGraphCornerX + yLableOffsetx;
        float x2 = mapWidth + mapGraphCornerX;
        float y = map( vertices[k][0], 0, theLargestValue, mapHeight + mapGraphCornerY, mapGraphCornerY );
        vertex( x1, y);
        vertex( x2, y);
      }
      else
      {
        float x1 = map( (rangeYears[0] + m), rangeYears[0], rangeYears[1], mapGraphCornerX + yLableOffsetx, mapWidth + mapGraphCornerX );
        float y1 = map( vertices[k][m], 0, theLargestValue, mapHeight + mapGraphCornerY, mapGraphCornerY );
        vertex( x1, y1);
        
        float x2 = map( (rangeYears[0] + m + 1), rangeYears[0], rangeYears[1], mapGraphCornerX + yLableOffsetx, mapWidth + mapGraphCornerX );
        float y2 = map( vertices[k][m + 1], 0, theLargestValue, mapHeight + mapGraphCornerY, mapGraphCornerY );
        vertex( x2, y2);
      }
    }
    endShape();
    
    //draw the region bounded by the line
    noStroke();
    fill( genreColors[selections[selections.length - 1 - k]], 100 );
    beginShape();
    for( int n = 0; n < vertices[k].length; n++)
    {
      if( vertices[k].length == 1)
      {
        float x1 = mapGraphCornerX + yLableOffsetx;
        float x2 = mapWidth + mapGraphCornerX;
        float y = map( vertices[k][0], 0, theLargestValue, mapHeight + mapGraphCornerY, mapGraphCornerY );
        vertex( x1, y);
        vertex( x2, y);
      }
      else
      {
        float x = map( (rangeYears[0] + n), rangeYears[0], rangeYears[1], mapGraphCornerX + yLableOffsetx, mapWidth + mapGraphCornerX );
        float y = map( vertices[k][n], 0, theLargestValue, mapHeight + mapGraphCornerY, mapGraphCornerY );
        vertex( x, y);
      }
    }
    if( k != 0)
    {
      int singleYearOffset = 1;
      if( vertices[k-1].length == 1)
      {
        singleYearOffset = 0;
      }
      for( int p = vertices[k-1].length-singleYearOffset; p >= 0; p--)
      {
        if(singleYearOffset == 0)
        {
          float x1 = mapWidth + mapGraphCornerX;
          float x2 = mapGraphCornerX + yLableOffsetx;
          float y = map( vertices[k - 1][0], 0, theLargestValue, mapHeight + mapGraphCornerY, mapGraphCornerY );
          vertex( x1, y);
          vertex( x2, y);
        }
        else
        {
          float x = map( (rangeYears[0] + p), rangeYears[0], rangeYears[1], mapGraphCornerX + yLableOffsetx, mapWidth + mapGraphCornerX );
          float y = map( vertices[k-1][p], 0, theLargestValue, mapHeight + mapGraphCornerY, mapGraphCornerY );
          vertex( x, y);
        }
      }
    }
    else
    {
      vertex( mapGraphCornerX + mapWidth, mapGraphCornerY + mapHeight);
      vertex( mapGraphCornerX + yLableOffsetx, mapGraphCornerY + mapHeight);
    }
    endShape(CLOSE);
  }
  int currentMouseX = mouseX;
  int currentMouseY = mouseY;
  if( (currentMouseX > mapGraphCornerX + yLableOffsetx) && (currentMouseX < mapGraphCornerX + mapWidth) )
  {
    int mouseYear = int(map( currentMouseX, mapGraphCornerX + yLableOffsetx, mapGraphCornerX + mapWidth, rangeYears[0], rangeYears[1]));
    if( (currentMouseY > mapGraphCornerY) && (currentMouseY < mapGraphCornerY + mapHeight) )
    {
      int mouseCount = int(map( currentMouseY, mapGraphCornerY + mapHeight, mapGraphCornerY, 0, theLargestValue));
      for( int mIndex = 0; mIndex < vertices.length; mIndex++)
      {
        if( mouseCount < vertices[mIndex][mouseYear-rangeYears[0]])
        {
          if(currentMouseX<200)
          {
            fill(100);
            rect(currentMouseX, currentMouseY - 40, 80, 40);
            fill(255);
            textAlign(CENTER);
            text(genres[selections[selections.length - mIndex - 1]], currentMouseX+40, currentMouseY-30);
            text(mouseYear, currentMouseX+40, currentMouseY-20);
            if(mIndex == 0)
              text((vertices[mIndex][mouseYear-rangeYears[0]]), currentMouseX+40, currentMouseY-10);
            else
              text(((vertices[mIndex][mouseYear-rangeYears[0]])-(vertices[mIndex-1][mouseYear-rangeYears[0]])), currentMouseX+40, currentMouseY-10);
            //println( genres[selections[selections.length - mIndex - 1]]);
             break;
          }
          else
          {
            fill(100);
            rect(currentMouseX-80, currentMouseY - 40, 80, 40);
            fill(255);
            textAlign(CENTER);
            text(genres[selections[selections.length - mIndex - 1]], currentMouseX-40, currentMouseY-30);
            text(mouseYear, currentMouseX-40, currentMouseY-20);
            if(mIndex == 0)
              text((vertices[mIndex][mouseYear-rangeYears[0]]), currentMouseX-40, currentMouseY-10);
            else
              text(((vertices[mIndex][mouseYear-rangeYears[0]])-(vertices[mIndex-1][mouseYear-rangeYears[0]])), currentMouseX-40, currentMouseY-10);
            //println( genres[selections[selections.length - mIndex - 1]]);
            break;
          }
        }
      }
    }
  }
}

void drawLabels( int yHigh)
{
  noStroke();
  fill( 0);
  rect( mapGraphCornerX, mapGraphCornerY, yLableOffsetx, mapHeight);
  int yInterval = 0;
  if( yHigh == 50)
  {
    yInterval = 10;
  }
  else if( yHigh == 200)
  {
    yInterval = 40;
  }
  else
  {
    int yScale = yHigh / 500;
    yInterval = yScale * 100;
  }
  fill(255);
  textAlign( RIGHT, CENTER);
  for( int v = yInterval; v < yHigh; v += yInterval)
  {
    float y = map(v, 0, yHigh, mapGraphCornerY + mapHeight, mapGraphCornerY);
    text( v, yLableOffsetx, y);
    stroke(0);
    strokeWeight(2);
    line( yLableOffsetx, y, yLableOffsetx+5, y);
  }
}

void drawAverage( float yUpperBound)
{
  beginShape(LINES);
    float y = map( theAverage, 0, yUpperBound, mapHeight + mapGraphCornerY, mapGraphCornerY);
    vertex( mapGraphCornerX + yLableOffsetx, y);
    vertex( mapGraphCornerX + mapWidth, y);
  endShape();
  fill(0);
  textAlign( LEFT, BOTTOM);
  text("Average", 10, y);
}

int[][] totalsPerYear()
{
  int theYear = 1920;
  int k = 0;
  String tempStr = "";
  int[][] tempTotals = new int [2010 - 1920][13];
  yearIndex = new int [tempTotals.length][];
  int[] monthIndex;

  for( int i = 0; i < tempTotals.length; i++)
  {
    monthIndex = new int[months.length];
    for( int j = 0; j < monthIndex.length; j++)
    {
      monthIndex[j] = k;
      while( ( k < data.getRowCount() ) && ( months[j].equalsIgnoreCase( data.getData( k, 1) ) ) )
      {
        
        if( !tempStr.equals( data.getData( k, 0) ) )
        {
          tempTotals[i][j]++;
          tempStr = data.getData( k, 0);
        }
        
        k++;
      }
    }
    yearIndex[i] = monthIndex;
    theYear++;
  }
  return tempTotals;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  this is the findData function
//
//////////////////////////////////////////////////////////////////////////////////////////////////////
int[] findData(String genre)
{
  int[] tempTotals = new int [90];
  int theYear = 1920;
  int j;
  if( clusterMode == yearsMode)
  {
    j = yearIndex[0][0];//finds the starting index for the first year in the range
  
    for( int i = 0; i < tempTotals.length; i++)
    {
      while( ( j < data.getRowCount() ) && ( parseInt( data.getData( j, 2) ) == theYear ) )
      {
        if( genre.equals( data.getData( j, 3) ) ) 
        {
          tempTotals[i]++;
        }
        j++;
      }
      theYear++;
    }
  }
  else if( clusterMode == monthsMode )
  {
    int beginningOfNextMonth;
//    int j = yearIndex[0][selectedMonth];//finds the starting index for the first year in the range
  
    for( int i = 0; i < tempTotals.length; i++)
    {
      beginningOfNextMonth = yearIndex[i][selectedMonth + 1];
      j = yearIndex[i][selectedMonth];
      while( ( j < data.getRowCount() ) && ( parseInt( data.getData( j, 2) ) == theYear ) && ( j < beginningOfNextMonth ) )
      {
        if( genre.equals( data.getData( j, 3) )  ) 
        {
          tempTotals[i]++;
        }
        j++;
      }
      theYear++;
    }
  }
  else if( clusterMode == seasonsMode )
  {
    int beginningOfNextSeason;
    if( selectedSeason != 3 )//for every season that isn't winter (which is spilt across two years)
    {
      for( int i = 0; i < tempTotals.length; i++)
      {
        beginningOfNextSeason = yearIndex[i][seasons[selectedSeason + 1]];
        j = yearIndex[i][seasons[selectedSeason]];
        while( ( j < data.getRowCount() ) && ( parseInt( data.getData( j, 2) ) == theYear ) && ( j < beginningOfNextSeason ) )
        {
          if( genre.equals( data.getData( j, 3) )  ) 
          {
            tempTotals[i]++;
          }
          j++;
        }
        theYear++;
      }
    }
    else
    {
      for( int i = 0; i < tempTotals.length; i++)
      {
        beginningOfNextSeason = yearIndex[i][seasons[0]];
        if( i == 0)
        {
          j = yearIndex[0][0];
        }
        else
        {
          j = yearIndex[i-1][seasons[selectedSeason]];
        }
        while( ( j < data.getRowCount() ) && ( j < beginningOfNextSeason ) )
        {
          if( ( genre.equals( data.getData( j, 3) )  ) && !( data.getData( j, 1).equalsIgnoreCase("rohan") ) )
          {
            tempTotals[i]++;
          }
          j++;
        }
        theYear++;
      }
    }
  }
  else
  {
    int foundIndex = 0;
    for( int i = 0; i < genres.length; i++)
    {
      if( genres[i].equals(genre) )
      {
        foundIndex = i;
      }
    }
    tempTotals = selectedGenreInfo[foundIndex];
  }
    
  return tempTotals;
}

int[] totalsPerYear(String genre1, String genre2)
{
  boolean foundOne = false;
  int theYear = 1920;
  int j = 0;
  int[] tempTotals = new int [2011 - 1920];
  String title = "";
  
  for( int i = 0; i < tempTotals.length; i++)
  {
    while( ( j < data.getRowCount() ) && ( parseInt( data.getData( j, 2) ) == theYear ) )
    {
      if( !title.equals( data.getData( j, 0) ) )
      {
        title = data.getData( j, 0);
        foundOne = false;
      }
      
      if( genre1.equals( data.getData( j, 3) ) || genre2.equals( data.getData( j, 3) ) ) 
      {
        if( foundOne)
        {
          tempTotals[i]++;
          foundOne = false;
        }
        else
        {
          foundOne = true;
        }
      }
      j++;
    }
    theYear++;
  }
  return tempTotals;
}

int findMax( int[] theArray)
{
  int theMax = 0;
  for( int i = 0; i < theArray.length; i++)
  {
    if( theMax < theArray[i])
    {
      theMax = theArray[i];
    }
  }
  return theMax;
}


//needs averages for months and seasons
float findAverage( int genre)
{
  float average = 0.0f;
  int sum = 0;
  float divisor = 2009 - 1920;
  
  for( int i  = 0; i < selectedGenreInfo[genre].length; i++)
  {
    sum += selectedGenreInfo[genre][i];
  }
  
  average = float(sum)/divisor;
  return average;
}

int findAdjustment( int theValue)
{
  if( theValue < 50)
  {
    return 50;
  }
  else if( theValue < 200)
  {
    return 200;
  }
  else
  {
    return 500;
  }
}
